from pathlib import Path



# Most Fundamental Scripts COMMON to ALL iPython Notebook
# Edith Specific
import os
import re
import math
nMPTasks = 4


# Data Analysis Libraries
import numpy as np
np.set_printoptions(suppress=True, linewidth=175)

from pandas import *; 
# set_option('display.notebook_repr_html', False)
set_option('display.notebook_repr_html', True)
set_option('display.float_format', lambda x: '%.3f' % x)
set_option('display.width',170,'display.max_rows',100)

# import statsmodels.formula.api as smf
from glob import glob
import itertools
from functools import reduce
from itertools import chain
from glob import glob
from timeit import default_timer as timer

def isnotebook():
    try:
        shell = get_ipython().__class__.__name__
        if shell == 'ZMQInteractiveShell':
            return True   # Jupyter notebook or qtconsole
        elif shell == 'TerminalInteractiveShell':
            return False  # Terminal running IPython
        else:
            return False  # Other type (?)
    except NameError:
        return False      # Probably standard Python interpreter
if isnotebook():
    import matplotlib.pyplot as plt
    import seaborn as sns; sns.set_style("whitegrid")

# Import PATHs of Projects
# P_PATH = P_Research/'iPython'/'Library'/'PATH_Projects.py'
# exec(compile(P_PATH.read_text(), P_PATH, 'exec'))
exec((P_Research/'Jupyter'/'library'/'PATH_Projects.py').read_text())

LOG_INPUT_MIN = 2.2250738585072014e-308
EXP_INPUT_MAX = 709.78271
TOL15 = 1e-15
TOL13 = 1e-13
TOL12 = 1e-12
TOL10 = 1e-10
TOL8 = 1e-8
TOL5 = 1e-5
TOL4 = 1e-4
TOL1 = 1e-1
ZEROS8 = int(1e8)

# Other Commonly Used Functions
def clipboard_list(ls):
    ls = [str(x) for x in ls]
    clipboard('\n'.join(ls))

def clipboard(obj):
    import pyperclip
    if type(obj)==str:
        pass
    elif type(obj)==list:
        obj = clipboard_list(obj)
    else:
        print('type error:',type(obj))
    pyperclip.copy(obj)
    spam = pyperclip.paste()
    
def flatten_list(ls):
    return list(chain.from_iterable(ls))

def pp2(df):
    print((df.head(2)))

def save_obj(var_name, f_path):
    import pickle
    with open(f_path, 'wb') as f:
        pickle.dump(var_name, f, pickle.HIGHEST_PROTOCOL)

def load_obj(f_path):
    import pickle
    with open(f_path, 'rb') as f:
        return pickle.load(f)

# Unique Data Types of A Series
def stypes(s):
    if type(s) == pandas.core.series.Series:
        print((s.apply(type).unique()))
    else:
        print('Input: Series Only')

def SQL_timeit(query):
    pre = 'set statistics time on \n'
    post = '\n set statistics time off'
    return pre + query + post

# Files Directory
def list_files(dir):
    r = []
    for root, dirs, files in os.walk(dir):
        for name in files:
            r.append(os.path.join(root, name))
    return r

# Print Pandas Tables Side by Side
# https://stackoverflow.com/questions/35790922/how-to-render-two-pd-dataframes-in-jupyter-notebook-side-by-side
def side_by_side(*objs, **kwds):
    ''' Une fonction print objects side by side '''
    from pandas.io.formats.printing import adjoin
    space = kwds.get('space', 4)
    reprs = [repr(obj).split('\n') for obj in objs]
    print(adjoin(space, *reprs))

# Computation
from math import log, pi, sqrt, exp
def float_to_str(v, nDecimal):
    if v is not None:
        return '{:.{prec}f}'.format(v, prec=nDecimal)
    else:
        return ''

def ls_elt_to_str(ls, nDecimal):
    return [float_to_str(x, nDecimal) for x in ls]

def array_to_string(arr, nDecimal=2):
    if type(arr)==np.ndarray:
        return ', '.join([f'{x:.2f}' for x in arr.tolist()])
    else:
        return ''

# NumPy Related Routines
def array_value_diff(arr1, arr2, diff_size=False):
    if diff_size:
        size_min = min(arr1.shape[0], arr2.shape[0])
        arr1 = arr1[:size_min]
        arr2 = arr2[:size_min]
    return np.absolute(arr1-arr2).sum()

def array_value_similar(arr1, arr2, tol=1e-2):
    abs_diff = np.absolute(arr1-arr2).sum()
    if abs_diff <= tol:
        return True
    else:
        return False

def open_file(f):
    if type(f)!=str:
        f = str(f)
    os.system("open " + f)

def reverse_dict(d):
    return {v: k for k, v in d.items()}

def split_list_into_chunks(ls, size):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(ls), size):
        yield ls[i:i + size]

def quote_f(f):
    return '"'+str(f)+'"'


def create_min_dash_max_pair(s):
    return s.min(axis=1).astype(str) + '-' + s.max(axis=1).astype(str)


def load_date_dictionary():
    # Input: Dates Dictionary
    global YMD
    YMD = read_hdf(P_Research / 'Others' / 'DateTime' / 'utils_dates_YMD.h5', 'utils')
    global dict_dtYMD_to_dtY6M
    dict_dtYMD_to_dtY6M = YMD.dtY6M.to_dict()
    global dict_dtYMD_to_YMID
    dict_dtYMD_to_YMID = YMD.y2kYM.to_dict()
    global dict_y2kYM_to_dtY6M
    dict_y2kYM_to_dtY6M = YMD[['y2kYM','dtY6M']].drop_duplicates().set_index('y2kYM').dtY6M.to_dict()
    global dict_y2kYM_to_dtYM
    dict_y2kYM_to_dtYM = YMD[['y2kYM','dtYM']].drop_duplicates().set_index('y2kYM').dtYM.to_dict()
    global dict_dtY6M_lastDay
    dict_dtY6M_lastDay = YMD.dtY6M_lastDay.drop_duplicates().to_dict()
    # Map Y6M to DateID: 2010/01/01-1; 2010/07/01-2, etc
    global dict_dtY6M_to_DateID, dict_DateID_to_dtY6M
    ls_dtY6M_unique = sorted(YMD.dtY6M.unique())
    dict_dtY6M_to_DateID = {y6m:i for i, y6m in enumerate(ls_dtY6M_unique)}
    dict_DateID_to_dtY6M = {i:y6m for i, y6m in enumerate(ls_dtY6M_unique)}
    # alternative way
    # dtY6M = sorted(YMD.dtY6M.unique())
    # dict_dtY6M_to_id = dict(zip(dtY6M, range(len(dtY6M))))
    # save_obj(dict_dtY6M_to_id, P_TA_Data / 'PyPI/dict_dtY6M_to_id.pkl')
    # # dict_dtY6M_to_id = load_obj(P_TA_Data / 'PyPI/dict_dtY6M_to_id.pkl')
